
PM_FUNC_NORM_ATTRIB PlayerResetInfo( Int:iPlayer )
{
	if ( iPlayer > g_iLargestPlayer )
		return;

//	log_amx( "g_iPlayerBInfo Size %d", sizeof( g_iPlayerBInfo[] ) );
//	log_amx( "g_iPlayerSInfo Size %d", sizeof( g_iPlayerSInfo[] ) * 4 );
//	log_amx( "g_iPlayerLInfo Size %d", sizeof( g_iPlayerLInfo[] ) );
	for ( new PINFO:iInfoNum; iInfoNum < PINFO; iInfoNum++ )
	{
		switch( iInfoNum )
		{
			case PINFO_BOOL_START..PINFO_BOOL_END:
			{
//				log_amx( "ResetBInfo %d", iInfoNum );
				g_iPlayerBInfo[ _:iInfoNum ] &= ~( 1 << ( iPlayer & 31 ) );
			}
			case PINFO_SHORT_START..PINFO_SHORT_END:
			{
//				log_amx( "ResetSInfo %d", iInfoNum - PINFO_SHORT_START );
				g_iPlayerSInfo[ iPlayer ]{ _:( iInfoNum - PINFO_SHORT_START ) } = SHORT_OFFSET;
			}
			case PINFO_LONG_START..PINFO_LONG_END:
			{
//				log_amx( "ResetLInfo %d", iInfoNum - PINFO_LONG_START );
				g_iPlayerLInfo[ iPlayer ][ _:( iInfoNum - PINFO_LONG_START ) ] = 0;
			}
		}
	}

	PlayerSetInfo( iPlayer, PINFO_BOOST_SKILL, SKILL_INVALID );
}

PM_FUNC_NORM_ATTRIB PlayerSetInfo( Int:iPlayer, PINFO:iInfoNum, any:iSetNum )
{
	if ( iPlayer > g_iLargestPlayer || ( !PlayerGetInfo( iPlayer, PINFO_CONNECT ) && iInfoNum != PINFO_CONNECT ) )
		return;

	switch( iInfoNum )
	{
		case PINFO_BOOL_START..PINFO_BOOL_END:
		{
			if ( iSetNum ) g_iPlayerBInfo[ _:iInfoNum ] |= ( 1 << ( iPlayer & 31 ) );
			else g_iPlayerBInfo[ _:iInfoNum ] &= ~( 1 << ( iPlayer & 31 ) );
		}
		case PINFO_SHORT_START..PINFO_SHORT_END:
		{
			g_iPlayerSInfo[ iPlayer ]{ _:( iInfoNum - PINFO_SHORT_START ) } = _:iSetNum + SHORT_OFFSET;
		}
		case PINFO_LONG_START..PINFO_LONG_END:
		{
			g_iPlayerLInfo[ iPlayer ][ _:( iInfoNum - PINFO_LONG_START ) ] = iSetNum;
		}
		default:
		{
			pokemod_debug( "PlayerSetInfo error. Attempted info %d set %d. Bools:%d-%d Shorts:%d-%d Longs:%d-%d", iInfoNum, iSetNum, PINFO_BOOL_START,PINFO_BOOL_END, PINFO_SHORT_START,PINFO_SHORT_END, PINFO_LONG_START,PINFO_LONG_END );
		}
	}
}

PM_FUNC_NORM_ATTRIB PlayerAddInfo( Int:iPlayer, PINFO:iInfoNum, Int:iAddNum )
{
	if ( iPlayer > g_iLargestPlayer || !iAddNum || ( !PlayerGetInfo( iPlayer, PINFO_CONNECT ) && iInfoNum != PINFO_CONNECT ) )
		return;

	switch( iInfoNum )
	{
		case PINFO_BOOL_START..PINFO_BOOL_END:
		{
			if ( iAddNum > 0 ) g_iPlayerBInfo[ _:iInfoNum ] |= ( 1 << ( iPlayer & 31 ) );
			else g_iPlayerBInfo[ _:iInfoNum ] &= ~( 1 << ( iPlayer & 31 ) );
		}
		case PINFO_SHORT_START..PINFO_SHORT_END:
		{
			g_iPlayerSInfo[ iPlayer ]{ _:( iInfoNum - PINFO_SHORT_START ) } += iAddNum;
		}
		case PINFO_LONG_START..PINFO_LONG_END:
		{
			g_iPlayerLInfo[ iPlayer ][ _:( iInfoNum - PINFO_LONG_START ) ] += iAddNum;
		}
		default:
		{
			pokemod_debug( "PlayerAddInfo error. Attempted info %d add %d. Bools:%d-%d Shorts:%d-%d Longs:%d-%d", iInfoNum, iAddNum, PINFO_BOOL_START,PINFO_BOOL_END, PINFO_SHORT_START,PINFO_SHORT_END, PINFO_LONG_START,PINFO_LONG_END );
		}
	}
}

PM_FUNC_NORM_ATTRIB PlayerGetInfo( Int:iPlayer, PINFO:iInfoNum )
{
	if ( iPlayer > g_iLargestPlayer )
		return 0;

	switch( iInfoNum )
	{
		case PINFO_BOOL_START..PINFO_BOOL_END:
		{
			return ( g_iPlayerBInfo[ _:iInfoNum ] & ( 1 << ( iPlayer & 31 ) ) ) ? 1 : 0;
		}
		case PINFO_SHORT_START..PINFO_SHORT_END:
		{
			return ( g_iPlayerSInfo[ iPlayer ]{ _:( iInfoNum - PINFO_SHORT_START ) } - SHORT_OFFSET );
		}
		case PINFO_LONG_START..PINFO_LONG_END:
		{
			return g_iPlayerLInfo[ iPlayer ][ _:( iInfoNum - PINFO_LONG_START ) ];
		}
	}
	pokemod_debug( "PlayerGetInfo error. Attempted info %d. Bools:%d-%d Shorts:%d-%d Longs:%d-%d", iInfoNum, PINFO_BOOL_START,PINFO_BOOL_END, PINFO_SHORT_START,PINFO_SHORT_END, PINFO_LONG_START,PINFO_LONG_END );
	return 0;
}

PM_FUNC_NORM_ATTRIB PlayerHUD( Int:iPlayer, Int:iWatchPlayer )
{
	pokemod_funcstart_debug( "PlayerHUD" );

	new Float:fGameTime = get_gametime();

	static Int:iHudColor;
	static String:szMessage[ 128 ], Int:iLen;
	static Int:iLocation;
	static Int:iEncounterID, Int:iEncounterLevel, String:szPokeName[ NAME_LEN ];
	static Int:iPokeNum, Int:iSkillNum;
	static Int:iPokeHP, Int:iLevel, Int:iLastLevelXP, Int:iXP, Int:iXPType;
	static PM_STATUS:iStatus, Int:iStatusAmount;

	if ( PlayerGetInfo( iPlayer, PINFO_BOT ) )
	{
		pokemod_funcend_debug( "PlayerHUD Bot" );

		return;
	}

	if ( PlayerGetInfo( iPlayer, PINFO_SETTING_HUD ) )
	{
		SetupShowHUD( iPlayer );
	}
	else if ( PlayerGetInfo( iPlayer, PINFO_SETTING_DHUD ) )
	{
		SetupShowDHUD( iPlayer );
	}
	else
	{
		if ( !is_user_alive( iWatchPlayer ) )
		{
			iWatchPlayer = iPlayer;
		}

		iLen = 0;

		//Status Change string
		if ( PlayerGetInfo( iWatchPlayer, PINFO_STATUS_EFFECT ) > 0 )
		{
			iLen += copy( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "Status: " );

			for ( iStatus = PM_STATUS:0; iStatus < PM_STATUS; iStatus++ )
			{
				iStatusAmount = g_iPlayerStatus[ iWatchPlayer ][ iStatus ][ STATUS_AMOUNT ];
				if ( iStatusAmount > 0 )
				{
					iLen += formatex( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "%s (%d) ", StatusToName( iStatus ), iStatusAmount );
				}
			}

			iLen += copy( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "^n" );
		}

		//Get the encounter here, because we don't want to update locations if fighting
		iEncounterID = PlayerGetInfo( iWatchPlayer, PINFO_ENCOUNTER_POKEMON );

		//Location string
		if ( PlayerGetInfo( iWatchPlayer, PINFO_TRAVELING ) && iEncounterID <= 0 )
		{
			iLocation = PlayerGetInfo( iWatchPlayer, PINFO_TRAVEL_TO );
			iLen += formatex( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "Traveling: %s (%d)^n", LocationToName( iLocation ), PlayerGetInfo( iWatchPlayer, PINFO_TRAVEL_TIME ) );
		}
		else
		{
			iLocation = PlayerGetInfo( iWatchPlayer, PINFO_LOCATION );
			iLen += formatex( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "Location: %s^n", LocationToName( iLocation ) );
		}

		//Encounter string
		if ( iEncounterID > 0 )
		{
			new Int:iEncProps = PlayerGetInfo( iWatchPlayer, PINFO_ENCOUNTER_PROPS );
			if ( iEncProps & ENCPROP_GHOST )
			{
				copy( szPokeName, charsmax(szPokeName), "Ghost" );
			}
			else
			{
				PokeToName( iEncounterID, szPokeName, charsmax(szPokeName) );
			}
			iEncounterLevel = PlayerGetInfo( iWatchPlayer, PINFO_ENCOUNTER_LEVEL );
			iLen += formatex( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "Fighting: %s (Lvl:%d) %s^n", szPokeName, iEncounterLevel, EncounterImage( iWatchPlayer, iEncounterID, iEncounterLevel ) );
		}

		//Pokemon string
		iPokeNum = PlayerGetInfo( iWatchPlayer, PINFO_ACTIVE_POKENUM );
		iLen += formatex( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "Pokemon: %s ", PokeNumToName( iPokeNum, .playerID = iWatchPlayer ) );

		if ( g_iPlayerPokemonStats[ iWatchPlayer ][ iPokeNum ][ POKESTAT_POKEMON ] > 0 )
		{
			iPokeHP = g_iPlayerPokemonStats[ iWatchPlayer ][ iPokeNum ][ POKESTAT_HP ];
			if ( !iPokeHP )
			{
				iLen += formatex( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "(Fainted)" );
			}
			else
			{
				iSkillNum = PlayerGetInfo( iWatchPlayer, PINFO_ACTIVE_SKILLNUM ) - 1;

				if ( g_fSkillReadyTime[ iWatchPlayer ] == SKILL_TIME_WAIT )
					iLen += copy( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "(Skl:Wait " );
				else if ( g_fSkillReadyTime[ iWatchPlayer ] > 0.0 )
					iLen += formatex( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "(Skl:%.01f ", g_fSkillReadyTime[ iWatchPlayer ] - fGameTime );
				else if ( iSkillNum > -1 && iSkillNum < SKILLS_PER_POKEMON && g_iPlayerSkillCharges[ iWatchPlayer ][ iSkillNum ] > 0 )
					iLen += formatex( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "(Skl:%d%%%% ", floatround( 100.0 * g_iPlayerSkillCharges[ iWatchPlayer ][ iSkillNum ] / SkillInfo( g_iPlayerPokemonSkills[ iWatchPlayer ][ iPokeNum ][ iSkillNum ], SI_CHARGES ) ) );
				else
					iLen += copy( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "(Skl:Ready " );

				iLevel = g_iPlayerPokemonStats[ iWatchPlayer ][ iPokeNum ][ POKESTAT_LEVEL ];
				iXP = g_iPlayerPokemonStats[ iWatchPlayer ][ iPokeNum ][ POKESTAT_XP ];
				iXPType = g_iPlayerPokemonStats[ iWatchPlayer ][ iPokeNum ][ POKESTAT_XP_TYPE ];
				iLastLevelXP = LevelToXP( iLevel - 1, iXPType );

				if ( PlayerGetInfo( iPlayer, PINFO_SETTING_XPNUM ) )
					iLen += formatex( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "Lvl:%d Exp:%d/%d HP:%d)", iLevel, iXP - iLastLevelXP, LevelToXP( iLevel, iXPType ) - iLastLevelXP, iPokeHP );
				else
					iLen += formatex( szMessage[ iLen ], ( charsmax(szMessage) - iLen ), "Lvl:%d Exp:%d%%%% HP:%d)", iLevel, floatround( 100.0 * ( iXP - iLastLevelXP ) / ( max( 1, LevelToXP( iLevel, iXPType ) - iLastLevelXP ) ), floatround_floor ), iPokeHP );
			}
		}

		iHudColor = PlayerGetInfo( iPlayer, PINFO_HUD_COLOR );
		if ( iHudColor == HUD_TEAM_BASED )
			iHudColor = _:cs_get_user_team( iWatchPlayer );

		set_hudmessage( .red = cHudColors[ iHudColor ][ 0 ],
						.green = cHudColors[ iHudColor ][ 1 ],
						.blue = cHudColors[ iHudColor ][ 2 ],
						.x = 0.01,
						.y = PlayerGetInfo( iPlayer, PINFO_HUDPOS ) * 0.01,
						.effects = 0,
						.fxtime = 0.1,
						.holdtime = 1.0,
						.fadeintime = 0.1,
						.fadeouttime = 0.1,
						.channel = -1
		);

		ShowSyncHudMsg( iPlayer, g_iHudSync, szMessage );
	}

	pokemod_funcend_debug( "PlayerHUD" );
}

PM_FUNC_NORM_ATTRIB PlayerHasPokemon( Int:iPlayer, Int:iPokeID )
{
	for ( new Int:iPokeNum; iPokeNum < POKEMON_PER_PLAYER; iPokeNum++ )
	{
		if ( g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_POKEMON ] == iPokeID )
		{
			return iPokeNum;
		}
	}
	return -1;
}
PM_FUNC_NORM_ATTRIB PlayerGivePoke( Int:iPlayer, Int:pokeID, Int:level = 1, Int:giverID = 0, Int:pokeIVs = 0 )
{
	if ( pokeID <= 0 )
		return false;

	pokemod_funcstart_debug( "PlayerGivePoke" );

	new Int:pokemonStats[ PM_POKESTAT ];
	pokemonStats[ POKESTAT_POKEMON ] = pokeID;

	new Int:xpType = random_num( 1, 6 );
	level = max( level, 1 );
	pokemonStats[ POKESTAT_LEVEL ] = level;
	pokemonStats[ POKESTAT_XP_TYPE ] = xpType;
	pokemonStats[ POKESTAT_XP ] = LevelToXP( level-1, xpType );

	pokemonStats[ POKESTAT_HP_EV ] = 0;
	pokemonStats[ POKESTAT_ATTACK_EV ] = 0;
	pokemonStats[ POKESTAT_DEFENSE_EV ] = 0;
	pokemonStats[ POKESTAT_SPECIAL_EV ] = 0;
	pokemonStats[ POKESTAT_SPEED_EV ] = 0;
	pokemonStats[ POKESTAT_IVS ] = pokeIVs ? pokeIVs : StatNewIVs();

	pokemonStats[ POKESTAT_HP ] = StatCalculate( .iStatNum = POKESTAT_HP_EV,
												.iPokeID = pokeID,
												.iLevel = level,
												.iEV = 0,
												.iIV = StatSingleIV( pokeIVs, POKESTAT_HP_EV )
												);

	pokemonStats[ POKESTAT_HAPPINESS ] = random_num( 10, 55 );

	new String:szPokeName[ NAME_LEN ];
	PokeToName( pokeID, szPokeName, charsmax(szPokeName) );

	new String:szOwnerName[ NAME_LEN ];
	PlayerToName( giverID ? giverID : iPlayer, szOwnerName, charsmax(szOwnerName) );

	new POKESKILL:pokemonSkills[ SKILLS_PER_POKEMON ];
	PokemonInitSkills( pokeID, level, pokemonSkills );

	new Int:skillPPs[ SKILLS_PER_POKEMON ];
	for ( new Int:iSkillNum, POKESKILL:skillID; iSkillNum < SKILLS_PER_POKEMON; iSkillNum++ )
	{
		skillID = pokemonSkills[ iSkillNum ];
		if ( skillID != SKILL_INVALID )
			skillPPs[ iSkillNum ] = SkillInfo( skillID, SI_PP );
	}

	//Try to find an empty slot for the pokemon
	new Int:iPokeNum = -1;
	for ( new Int:iTempPokeNum; iTempPokeNum < POKEMON_PER_PLAYER; iTempPokeNum++ )
	{
		if ( g_iPlayerPokemonStats[ iPlayer ][ iTempPokeNum ][ POKESTAT_POKEMON ] <= 0 )
		{
			iPokeNum = iTempPokeNum;
			break;
		}
	}

	new String:szSaveName[ NAME_LEN ];
	//If we didn't find an empty slot, find a box and slot to put the pokemon in
	if ( iPokeNum == -1 )
	{
		new bool:foundEmptyBox = false;
		for ( new Int:boxNum, Int:slotNum;
			!foundEmptyBox;
			boxNum++
		){
			for ( slotNum = 0; slotNum < POKEMON_PER_PLAYER; slotNum++ )
			{
				SaveNameBoxSlot( szSaveName, charsmax(szSaveName), boxNum, slotNum );
				if ( !player_get_data( iPlayer, szSaveName, gPokeStatNames[ POKESTAT_POKEMON ] ) )
				{
					foundEmptyBox = true;
					DHudEventMessage( iPlayer, "%s was sent to Emp`s PC (Box %d)", szPokeName, boxNum + 1 );
					break;
				}
			}
			if ( foundEmptyBox )
				break;
		}
	}
	//If we did find a slot to slip the pokemon into, let's load the stats/skills/etc. into the slot
	else
	{
		SaveNameSlot( szSaveName, charsmax(szSaveName), iPokeNum );

		copy( g_szPlayerPokemonNames[ iPlayer ][ iPokeNum ], charsmax( g_szPlayerPokemonNames[][] ), szPokeName );
		for ( new PM_POKESTAT:iStatNum; iStatNum < PM_POKESTAT; iStatNum++ )
		{
			g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ iStatNum ] = pokemonStats[ iStatNum ];
		}
		for ( new Int:iSkillNum; iSkillNum < SKILLS_PER_POKEMON; iSkillNum++ )
		{
			g_iPlayerPokemonSkills[ iPlayer ][ iPokeNum ][ iSkillNum ] = pokemonSkills[ iSkillNum ];
			g_iPlayerSkillPP[ iPlayer ][ iPokeNum ][ iSkillNum ] = skillPPs[ iSkillNum ];
		}

		// If it is their first pokemon
		if ( iPokeNum == 0 )
		{
			SkillUpdateCalls();
			PlayerLoadStats( iPlayer, iPokeNum, .setHP = false );
		}
	}

	SavePlayerPokemon( iPlayer, .szSave = szSaveName,
							.szPokeName = szPokeName,
							.iPokemonStats = pokemonStats,
							.iPokemonSkills = pokemonSkills,
							.iSkillPPs = skillPPs,
							.szOwnerName = szOwnerName
	);

	pokemod_funcend_debug( "PlayerGivePoke" );

	return true;
}
PM_FUNC_NORM_ATTRIB PlayerGiveXP( iPlayer, iPokeNum, iGiveXP )
{
	if ( iPokeNum < 0 )
		return 0;

	new Int:iPokeID = g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_POKEMON ];

	if ( iPokeID <= 0 )
		return 0;

	pokemod_funcstart_debug( "PlayerGiveXP" );

	new Int:iOldXP = g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_XP ];
	new Int:iNewXP = max( iOldXP + iGiveXP, 1 );
	new Int:iXPType = g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_XP_TYPE ];

	g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_XP ] = iNewXP;
	SavePlayerPokeStat( iPlayer, iPokeNum, POKESTAT_XP );

	new Int:iOldLevel = LevelFromXP( iOldXP, iXPType );
	new Int:iNewLevel = LevelFromXP( iNewXP, iXPType );
	if ( iNewLevel > iOldLevel )
	{
		DHudEventMessage( iPlayer, "%s is now level %d!", PokeNumToName( iPokeNum, .playerID = iPlayer ), iNewLevel );

		g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_LEVEL ] = iNewLevel;
		SavePlayerPokeStat( iPlayer, iPokeNum, POKESTAT_LEVEL );

		//ding for level up
		SoundEmit( iPlayer, SOUND_BELL );

		if ( is_user_alive( iPlayer ) )
		{
			new Coord:cOrigin[3];
			get_user_origin( iPlayer, cOrigin )

			//sprites to show level up (only once)
			message_begin( MSG_BROADCAST, SVC_TEMPENTITY );
			write_byte( TE_LARGEFUNNEL );
			write_coord( cOrigin[0] ); // funnel position
			write_coord( cOrigin[1] );
			write_coord( cOrigin[2] - 32 );
			write_short( SPRITE_INDEX[ SPRITE_MUZZLEFLASH1 ] ); // sprite index
			write_short( 8 ); // flags
			message_end();
		}

		new String:szLevel[ 16 ];
		new Int:iTell, Int:iAssumedTell = ArrayGetCell( g_iPokemonTells, iPokeID );

		g_iScriptPokeNum = iPokeNum;

		for ( new Int:level = iOldLevel + 1; level < iNewLevel; level++ )
		{
			num_to_str( level, szLevel, charsmax(szLevel) );
			FileScript( iPlayer, FileName( FILE_POKEMON ), PokeToName( iPokeID ), "LevelUp", szLevel, iAssumedTell, iTell );
		}

		num_to_str( iNewLevel, szLevel, charsmax(szLevel) );
		FileScript( iPlayer, FileName( FILE_POKEMON ), PokeToName( iPokeID ), "LevelUp", szLevel, iAssumedTell, iTell, true );

		g_iScriptPokeNum = -1;

		if ( iAssumedTell != iTell )
			ArraySetCell( g_iPokemonTells, iPokeID, iTell );
	}
	else if ( iNewLevel < iOldLevel )
	{
		DHudEventMessage( iPlayer, "%s is now level %d.", PokeNumToName( iPokeNum, .playerID = iPlayer ), iNewLevel );

		g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_LEVEL ] = iNewLevel;
		SavePlayerPokeStat( iPlayer, iPokeNum, POKESTAT_LEVEL );
	}

	PlayerLoadStats( iPlayer, iPokeNum, .setHP = false );

	pokemod_funcend_debug( "PlayerGiveXP" );

	return 1;
}

PM_FUNC_NORM_ATTRIB PlayerHasSkill( Int:iPlayer, Int:iPokeNum, POKESKILL:iSkillID )
{
	if ( iSkillID <= SKILL_INVALID )
		return -1;

	new bool:bCheckAll = ( iPokeNum == -1 );
	for ( new Int:iSkillNum; iSkillNum < SKILLS_PER_POKEMON; iSkillNum++ )
	{
		if ( bCheckAll )
		{
			for ( iPokeNum = 0; iPokeNum < POKEMON_PER_PLAYER; iPokeNum++ )
			{
				if ( iSkillID == g_iPlayerPokemonSkills[ iPlayer ][ iPokeNum ][ iSkillNum ] )
				{
					return iSkillNum;
				}
			}
		}
		else
		{
			if ( iSkillID == g_iPlayerPokemonSkills[ iPlayer ][ iPokeNum ][ iSkillNum ] )
			{
				return iSkillNum;
			}
		}
	}

	return -1;
}

PM_FUNC_NORM_ATTRIB PlayerHasItem( Int:iPlayer, Int:iItemID, &Int:itemAmount = 0 )
{
	if ( iItemID <= -1 )
		return -1;
	new Array:playerItems = g_iPlayerItems[ iPlayer ];
	for ( new Int:itemNum, Int:size = ArraySize( playerItems ) / _:PLAYER_ITEM;
		itemNum < size;
		itemNum++
	){
		if ( ArrayGetCell( playerItems, ( ( itemNum * _:PLAYER_ITEM ) + _:ITEM_ID ) ) == iItemID )
		{
			itemAmount = ArrayGetCell( playerItems, ( ( itemNum * _:PLAYER_ITEM ) + _:ITEM_AMOUNT ) );
			if ( itemAmount > 0 )
				return itemNum;
		}
	}
	itemAmount = 0;
	return -1;
}
PM_FUNC_NORM_ATTRIB PlayerGiveItem( Int:iPlayer, Int:iItemID, Int:amount, bool:save = true )
{
	if ( iItemID <= -1 )
		return 0;

	pokemod_funcstart_debug( "PlayerGiveItem" );

	new Array:playerItems = g_iPlayerItems[ iPlayer ];
	for ( new Int:itemNum, Int:itemAmount, Int:size = ArraySize( playerItems ) / _:PLAYER_ITEM;
		itemNum < size;
		itemNum++
	){
		if ( ArrayGetCell( playerItems, ( ( itemNum * _:PLAYER_ITEM ) + _:ITEM_ID ) ) == iItemID )
		{
			itemAmount = ArrayGetCell( playerItems, ( ( itemNum * _:PLAYER_ITEM ) + _:ITEM_AMOUNT ) );
			if ( itemAmount + amount > 0 )
			{
				ArraySetCell( playerItems, ( ( itemNum * _:PLAYER_ITEM ) + _:ITEM_AMOUNT ), itemAmount + amount );
				if ( save )
				{
					player_set_num( iPlayer, "PokeItems", ItemToName( iItemID ), itemAmount + amount );
				}
			}
			else
			{
				for ( new PLAYER_ITEM:i; i < PLAYER_ITEM; i++ )
				{
					ArrayDeleteItem( playerItems, ( itemNum * _:PLAYER_ITEM ) );
				}
				if ( save )
				{
					player_take_data( iPlayer, "PokeItems", ItemToName( iItemID ) );
				}

			}

			pokemod_funcend_debug( "PlayerGiveItem Already had item" );

			return 1;
		}
	}

	if ( amount > 0 )
	{
		new Int:itemInfo[ PLAYER_ITEM ];
		itemInfo[ ITEM_ID ] = iItemID;
		itemInfo[ ITEM_AMOUNT ] = amount;

		for ( new PLAYER_ITEM:i; i < PLAYER_ITEM; i++ )
		{
			ArrayPushCell( playerItems, itemInfo[ i ] );
		}

		if ( save )
			player_set_num( iPlayer, "PokeItems", ItemToName( iItemID ), amount );
	}

	pokemod_funcend_debug( "PlayerGiveItem" );

	return 1;
}

PM_FUNC_NORM_ATTRIB PlayerHasSpecial( Int:iPlayer, String:szSpecial[] )
{
	return TrieKeyExists( g_iPlayerSpecials[ iPlayer ], szSpecial );
}
PM_FUNC_NORM_ATTRIB PlayerGiveSpecial( Int:iPlayer, String:szSpecial[], bool:save = true )
{
	if ( save )
		player_set_data( iPlayer, "PokeModSpecial", szSpecial, "1" );
	return TrieSetCell( g_iPlayerSpecials[ iPlayer ], szSpecial, 1 );
}
PM_FUNC_NORM_ATTRIB PlayerTakeSpecial( Int:iPlayer, String:szSpecial[] )
{
	player_take_data( iPlayer, "PokeModSpecial", szSpecial );
	return TrieDeleteKey( g_iPlayerSpecials[ iPlayer ], szSpecial );
}


PM_FUNC_NORM_ATTRIB PlayerGetMoney( Int:iPlayer )
{
	#if defined money_ul_included
		if ( g_iPluginUnlimMoney != -1 )
			return cs_get_user_money_ul( iPlayer );
	#endif
	return cs_get_user_money( iPlayer );
}
PM_FUNC_NORM_ATTRIB PlayerGiveMoney( Int:iPlayer, Int:iAmount, bool:bFlash = true )
{
	new Int:iMoney = PlayerGetMoney( iPlayer ) + iAmount;
	#if defined money_ul_included
		if ( g_iPluginUnlimMoney != -1 )
			return cs_set_user_money_ul( iPlayer, iMoney, bFlash );
	#endif
	return cs_set_user_money( iPlayer, clamp( iMoney, 0, 16000 ), bFlash );
}
PM_FUNC_NORM_ATTRIB PlayerSetMoney( Int:iPlayer, Int:iMoney, bool:bFlash = true )
{
	#if defined money_ul_included
		if ( g_iPluginUnlimMoney != -1 )
			return cs_set_user_money_ul( iPlayer, iMoney, bFlash );
	#endif
	return cs_set_user_money( iPlayer, iMoney, bFlash );
}

PM_FUNC_NORM_ATTRIB PlayerChangePokemon( Int:iPlayer, Int:iPokeNum, bool:bForced )
{
	if ( PlayerGetInfo( iPlayer, PINFO_ACTIVE_POKENUM ) == iPokeNum )
		return false;

	if ( g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_POKEMON ] <= 0 )
		return false;

	if ( g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_HP ] <= 0 )
	{
		if ( !bForced )
			DHudEventMessage( iPlayer, "%s has fainted!", PokeNumToName( iPokeNum, .playerID = iPlayer ) );
		return false;
	}

	pokemod_funcstart_debug( "PlayerChangePokemon" );

	if ( is_user_alive( iPlayer ) )
	{
		SkillCallType( CALL_RESET, iPlayer, .bCheckReady = false );
	}

	PlayerSetInfo( iPlayer, PINFO_ACTIVE_POKENUM, iPokeNum );
	PlayerLoadStats( iPlayer, iPokeNum, .setHP = false );

	SkillUpdateCalls();

	new String:szMessages[][] = {
		"Go %s!",
		"I choose you %s!",
		"Get 'em %s!",
		"Time to fight %s!",
		"Show your strength %s!",
		"%s you can do it!"
	}

	DHudEventMessage( iPlayer, szMessages[ random( sizeof szMessages ) ], PokeNumToName( iPokeNum, .playerID = iPlayer ) );

	SkillAddDelay( iPlayer, SettingGetFloat( CHANGE_DELAY ) );

	pokemod_funcend_debug( "PlayerChangePokemon" );

	return true;
}

PM_FUNC_NORM_ATTRIB PlayerLoadStats( Int:iPlayer, Int:iPokeNum, bool:setHP )
{
	pokemod_funcstart_debug( "PlayerLoadStats" );

	new Int:iPokeID = g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_POKEMON ];
	new Int:iLevel = g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_LEVEL ];
	new Int:pokeIVs = g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_IVS ];

	PlayerSetInfo( iPlayer, PINFO_TYPE, _:PokemonType( iPokeID ) );

	new Int:iTrainBoost = PlayerGetInfo( iPlayer, PINFO_TRAINING_BOOST );

	for ( new Int:iTempStat, PM_STAT:iStatNum = STAT_ATTACK, PM_POKESTAT:iPokeStat; iStatNum < STAT_HP; iStatNum++ )
	{
		iPokeStat = PlayerStatToPokeStat( iStatNum );
		if ( iPokeStat <= POKESTAT_NULL )
			continue;

		iTempStat = StatCalculate( iPokeStat, iPokeID, iLevel, g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ iPokeStat ], StatSingleIV( pokeIVs, iPokeStat ) );

		if ( iTempStat < g_iMaxStat )
			iTempStat = min( g_iPlayerStats[ iPlayer ][ iStatNum ] + iTrainBoost, g_iMaxStat );

		g_iPlayerStats[ iPlayer ][ iStatNum ] = iTempStat;
	}

	PlayerStatHUD( iPlayer );

	if ( setHP )
	{
		new Int:iMaxHealth = 100 + iLevel;
		set_pev( iPlayer, pev_max_health, float( iMaxHealth ) );
		set_user_health( iPlayer, iMaxHealth );
	}
	else
	{
		new Int:iMaxHealth = 100 + iLevel;
		set_pev( iPlayer, pev_max_health, float( iMaxHealth ) );
		if ( get_user_health( iPlayer ) > iMaxHealth )
			set_user_health( iPlayer, iMaxHealth );
	}

	for ( new Int:iSkillNum; iSkillNum < SKILLS_PER_POKEMON; iSkillNum++ )
	{
		g_iPlayerSkillVariable[ iPlayer ][ iSkillNum ] = 0;
		g_vPlayerSkillVector[ iPlayer ][ iSkillNum ] = Float:{0.0,0.0,0.0};
	}

	pokemod_funcend_debug( "PlayerLoadStats" );
}

PM_FUNC_NORM_ATTRIB PlayerWeaponEnt( Int:iPlayer, Int:iWeaponID )
{
	if ( is_user_alive( iPlayer ) && iWeaponID != 2 )
	{
		new String:szWeapName[32];
		get_weaponname( iWeaponID, szWeapName, charsmax(szWeapName) );
		new Int:iEntity;
		while ( ( iEntity = engfunc( EngFunc_FindEntityByString, iEntity, "classname", szWeapName ) ) && pev( iEntity, pev_owner ) != iPlayer ) {}
		return iEntity;
	}

	return 0;
}

PM_FUNC_NORM_ATTRIB PlayerViewMap( Int:iPlayer, iLocation = -1 )
{
	pokemod_funcstart_debug( "PlayerViewMap" );

	if ( iLocation == -1 )
		iLocation = PlayerGetInfo( iPlayer, PINFO_LOCATION );

	new String:szLocation[ NAME_LEN ];
	LocationToName( iLocation, szLocation, charsmax(szLocation) );

	new String:szMOTD[ 1024 ], Int:iLen;
	new String:szData[ 128 ];

	iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<center><table border=^"1^"><tr><td>" );

	new String:szVersion[ NAME_LEN ];
	copy( szVersion, charsmax(szVersion), szPokeModVersion[ PlayerGetInfo( iPlayer, PINFO_VERSION ) ] );

	new String:szFileName[ 64 ];
	copy( szFileName, charsmax(szFileName), FileName( FILE_LOCATIONS ) );
	new Int:nameLine, Int:iLoc, Int:iContainLoc;
	new String:szName[ NAME_LEN ], String:szLevel[ 16 ], String:szChance[ 16 ];
	while ( nfv_get_linedata( szFileName, szLocation, NFV_WILDCARD, szData, charsmax( szData ),
			.name_line_start = ( nameLine + 1 ), .name_line_find = nameLine,
			.identifier = '#'
			)
	){
		replace_all( szData, charsmax(szData), "(", " " );

		if ( ( iContainLoc = contain( szData, "+Version" ) ) > -1 )
		{
			if ( contain( szData[ iContainLoc ], szVersion ) == -1 )
				continue;
		}
		else if ( ( iContainLoc = contain( szData, "-Version" ) ) > -1 )
		{
			if ( contain( szData[ iContainLoc ], szVersion ) != -1 )
				continue;
		}

		iLoc = 0;
		while ( ( iContainLoc = contain( szData[ iLoc ], "EncounterPokemon" ) ) > -1 )
		{
			iLoc += iContainLoc + 16;

			strtok( szData[ iLoc ], szName, charsmax(szName), szData[ iLoc ], charsmax(szData) - iLoc, ',', 0 );
			strtok( szData[ iLoc ], szLevel, charsmax(szLevel), szData[ iLoc ], charsmax(szData) - iLoc, ',', 0 );
			strtok( szData[ iLoc ], szChance, charsmax(szChance), szData[ iLoc ], charsmax(szData) - iLoc, ')', 0 );
			trim( szName );
			trim( szLevel );
			trim( szChance );

			iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "%s</td><td>%s</td><td>%s</td></tr><tr><td>", szName, szLevel, szChance );
		}

		iLoc = 0;
		while ( ( iContainLoc = contain( szData[ iLoc ], "FindItem" ) ) > -1 )
		{
			iLoc += iContainLoc + 8;

			copyc( szName, charsmax(szName), szData[ iLoc ], ')' );
			trim( szName );

			iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "Item</td><td>%s</td><td></td></tr><tr><td>", szName );
		}
	}

	//End pokemon/item table
	iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "</tr></table>" );

	//Start image map
	iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<br><br>" );

	nfv_get_data( FileName( FILE_LOCATIONS ), szLocation, "Map", szData, charsmax(szData), '#' );
	if ( strlen( szData ) > 0 )
		iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<img src=^"%s^" width=^"100%%^">", szData );
	else
		iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "No map image available for %s.", szLocation );
	//End image map

	//Finish the motd
	iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "</center>" );

	show_motd( iPlayer, szMOTD, szLocation );

	pokemod_funcend_debug( "PlayerViewMap" );
}

PM_FUNC_NORM_ATTRIB PlayerRelease( Int:iPlayer, Int:iPokeNum )
{
	pokemod_funcstart_debug( "PlayerRelease" );

	DHudEventMessage( iPlayer, "%s has been released into the wild.", PokeNumToName( iPokeNum, .playerID = iPlayer ) );

	new String:szSaveName[ NAME_LEN ];
	SaveNameSlot( szSaveName, charsmax(szSaveName), iPokeNum );

	player_remove_key( iPlayer, szSaveName );

	g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_POKEMON ] = 0;

	pokemod_funcend_debug( "PlayerRelease" );
}

PM_FUNC_NORM_ATTRIB PlayerAimOrigin( Int:iPlayer, Coord:cAimOrigin[3], Int:iDist = -1 )
{
	pokemod_funcstart_debug( "PlayerAimOrigin" );

	get_user_origin( iPlayer, cAimOrigin, 3 );
	if ( iDist > 0 )
	{
		new Coord:cOrigin[3];
		get_user_origin( iPlayer, cOrigin );
		if ( get_distance( cOrigin, cAimOrigin ) > iDist )
		{
			//Get the vector from Origin to Aim
			new Vector:vVector[3];
			vVector[0] = float( cAimOrigin[0] - cOrigin[0] );
			vVector[1] = float( cAimOrigin[1] - cOrigin[1] );
			vVector[2] = float( cAimOrigin[2] - cOrigin[2] );

			//Get the length so we can make it a unit vector
			new Float:fVecLen = vector_length( vVector );

			//Make it a unit vector and then extend it to length iDist
			vVector[0] = iDist * vVector[0] / fVecLen;
			vVector[1] = iDist * vVector[1] / fVecLen;
			vVector[2] = iDist * vVector[2] / fVecLen;

			//Final origin will be the player's origin + the vector
			cAimOrigin[0] = floatround( cOrigin[0] + vVector[0] );
			cAimOrigin[1] = floatround( cOrigin[1] + vVector[1] );
			cAimOrigin[2] = floatround( cOrigin[2] + vVector[2] );
		}
	}

	pokemod_funcend_debug( "PlayerAimOrigin" );
}

PM_FUNC_NORM_ATTRIB PlayerSkillHelp( Int:iPlayer, POKESKILL:skillID )
{
	pokemod_funcstart_debug( "PlayerSkillHelp" );

	new String:szSkill[ NAME_LEN ];
	SkillToName( skillID, szSkill, charsmax(szSkill) );

	new String:szMOTD[ 1024 ], Int:iLen;

	iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<center><b>%s</b><br><br>", szSkill );

	iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "%s", SkillGetHelp( skillID ) );

	iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "</center>" );

	show_motd( iPlayer, szMOTD, szSkill );

	pokemod_funcend_debug( "PlayerSkillHelp" );
}

PM_FUNC_NORM_ATTRIB PlayerPokedex( Int:iPlayer, Int:iPokeID )
{
	pokemod_funcstart_debug( "PlayerPokedex" );

	new String:szPoke[ NAME_LEN ];
	PokeToName( iPokeID, szPoke, charsmax(szPoke) );

	new String:szMOTD[ 2048 ], Int:iLen;

	iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<html><body><center><table><tr><td><center><font size=^"6^">%s</font><br>", szPoke );
	iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<img src=^"%s^"><br>", PokemonInfo( iPokeID, "ImageL" ) );

	new String:szData[ 256 ], String:szLeft[ 64 ], String:szRight[ 256 ];
	new String:szStatList[ 512 ], Int:iStats;
	new String:szLevelList[ 512 ], Int:iLevels;
	new String:szItemList[ 512 ], Int:iItems;
	new Int:iLoc;

	new String:szFileName[ 64 ];
	copy( szFileName, charsmax(szFileName), FileName( FILE_POKEMON ) );

	new Int:fileHandle = fopen( FileName( FILE_POKEMON ), "rt" );

	new Int:iTell, Int:iAssumedTell = ArrayGetCell( g_iPokemonTells, iPokeID );

	if ( !FileSeekToName( fileHandle, PokeToName( iPokeID ), iAssumedTell, iTell ) )
	{
		pokemod_funcend_debug( "PlayerPokedex unable to find" );
		return;
	}

	if ( iAssumedTell != iTell )
		ArraySetCell( g_iPokemonTells, iPokeID, iTell );

	while ( !feof( fileHandle ) )
	{
		fgets( fileHandle, szData, charsmax(szData) );
		trim( szData );

		if ( szData[ 0 ] == '#' )
			break;

		if ( szData[ 0 ] == ';' || ( szData[ 0 ] == '/' && szData[ 1 ] == '/' ) )
			continue;

		if ( equal( szData, "Image", 5 ) )
			continue;

		if ( equal( szData, "Pokedex ", 8 ) )
		{
			iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "%s<br>", szData[ 8 ] );
			continue;
		}

		else if ( equal( szData, "Init ", 5 ) )
		{
			if ( ( iLoc = contain( szData[ 5 ], "LearnSkill(" ) ) == -1 )
				continue;

			if ( iLevels )
			{
				if ( iLevels % 4 == 0 )
					add( szLevelList, charsmax(szLevelList), "</td><td>");
				else
					add( szLevelList, charsmax(szLevelList), "<br>");
			}

			iLoc += 5;
			replace( szData[ iLoc ], charsmax(szData) - iLoc, "LearnSkill(", "" );
			replace( szData[ iLoc ], charsmax(szData) - iLoc, ")", "^0" );
			trim( szData[ iLoc ] );
			formatex( szRight, charsmax(szRight), "Lvl 1 - %s", szData[ iLoc ] );

			trim( szRight );

			add( szLevelList, charsmax(szLevelList), szRight );

			iLevels++;
		}
		else if ( equal( szData, "LevelUp(", 8 ) )
		{
			if ( ( iLoc = contain( szData[ 8 ], "LearnSkill(" ) ) != -1 )
			{
				copyc( szLeft, charsmax(szLeft), szData[ 8 ], ')' );
				trim( szLeft );

				if ( iLevels )
				{
					if ( iLevels % 4 == 0 )
						add( szLevelList, charsmax(szLevelList), "</td><td>");
					else
						add( szLevelList, charsmax(szLevelList), "<br>");
				}

				iLoc += 8;
				replace( szData[ iLoc ], charsmax(szData) - iLoc, "LearnSkill(", "" );
				replace( szData[ iLoc ], charsmax(szData) - iLoc, ")", "^0" );
				trim( szData[ iLoc ] );
				formatex( szRight, charsmax(szRight), "Lvl %s - %s", szLeft, szData[ iLoc ] );

				trim( szRight );

				add( szLevelList, charsmax(szLevelList), szRight );

				iLevels++;
			}
			if ( ( iLoc = contain( szData[ 8 ], "Evolve(" ) ) != -1 )
			{
				copyc( szLeft, charsmax(szLeft), szData[ 8 ], ')' );
				trim( szLeft );

				if ( iLevels )
				{
					if ( iLevels % 4 == 0 )
						add( szLevelList, charsmax(szLevelList), "</td><td>");
					else
						add( szLevelList, charsmax(szLevelList), "<br>");
				}

				iLoc += 8;
				replace( szData[ iLoc ], charsmax(szData) - iLoc, "Evolve(", "" );
				replace( szData[ iLoc ], charsmax(szData) - iLoc, ")", "^0" );
				trim( szData[ iLoc ] );
				formatex( szRight, charsmax(szRight), "Lvl %s - Evolve %s", szLeft, szData[ iLoc ] );

				trim( szRight );

				add( szLevelList, charsmax(szLevelList), szRight );

				iLevels++;
			}
		}
		else if ( equal( szData, "GiveItem(", 9 ) )
		{
			copyc( szLeft, charsmax(szLeft), szData[ 9 ], ')' );
			trim( szLeft );

			if ( iItems )
			{
				if ( iItems % 10 == 0 )
					add( szItemList, charsmax(szItemList), "<br>");
				else
					add( szItemList, charsmax(szItemList), ", ");
			}

			add( szItemList, charsmax(szItemList), szLeft );

			iItems++;
		}
		else
		{
			if ( iStats )
				add( szStatList, charsmax(szStatList), "<br>" );
			add( szStatList, charsmax(szStatList), szData );
			iStats++;
		}
	}

	fclose( fileHandle );

	if( iLevels )
	{
		iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<table border=^"1^"><tr><td>%s</td></tr></table><br>", szLevelList );
	}
	if( iItems )
	{
		iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<table border=^"1^"><tr><th>Can Use:</th></tr><tr><td>%s</td></tr></table>", szItemList );
	}

	iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<table><tr><th>" );

	new Int:iMaxPokes = ArraySize( g_iPokemonTells ) - 1;
	new Int:iStart = clamp( iPokeID - 3, 1, iMaxPokes );
	new Int:iEnd = clamp( iPokeID + 3, 1, iMaxPokes );
	for ( iPokeID = iStart; iPokeID <= iEnd; iPokeID++ )
	{
		iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<img src=^"%s^"><br>%d. %s</th><th>", PokemonInfo( iPokeID, "ImageS" ), iPokeID, PokeToName( iPokeID ) );
	}

	iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "</th></tr></table></td><td>" );

	if( iStats )
	{
		iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<td><table border=^"1^"><tr><td>%s</td></tr></table></td>", szStatList );
	}

	iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "</body></html>" );

	show_motd( iPlayer, szMOTD, szPoke );

	pokemod_funcend_debug( "PlayerPokedex" );
}

PM_FUNC_NORM_ATTRIB PlayerPokemonNum( Int:iPlayer, Int:iPokemon, Int:iLevel = -1 )
{
	pokemod_funcstart_debug( "PlayerPokemonNum" );

	new Int:iPokeNum = -1;
	for ( new Int:iTempPokeNum; iTempPokeNum < POKEMON_PER_PLAYER; iTempPokeNum++ )
	{
		if ( g_iPlayerPokemonStats[ iPlayer ][ iTempPokeNum ][ POKESTAT_POKEMON ] == iPokemon )
		{
			if ( !iPokemon || iLevel == -1 || g_iPlayerPokemonStats[ iPlayer ][ iTempPokeNum ][ POKESTAT_LEVEL ] == iLevel )
			{
				iPokeNum = iTempPokeNum;
				break;
			}
		}
	}

	pokemod_funcend_debug( "PlayerPokemonNum" );

	return iPokeNum;
}

PM_FUNC_NORM_ATTRIB PlayerViewPlayers( Int:iPlayer, String:szTitle[], Int:iPlayers[ MAX_PLAYERS ], Int:iPlayerCount )
{
	pokemod_funcstart_debug( "PlayerViewPlayers" );

	new String:szMOTD[ 1024 ], Int:iLen;

	iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<center><b>%s</b><br><br>", szTitle );

	new Int:iViewPlayer;
	new Int:iPokeID;
	new Int:iActivePoke;
	if ( iPlayerCount == 1 )
	{
		iViewPlayer = iPlayers[ 0 ];
		iActivePoke = PlayerGetInfo( iViewPlayer, PINFO_ACTIVE_POKENUM );

		new Int:iPokeCount;
		new POKESKILL:iSkillID, Int:iSkillCount;

		for ( new Int:iPokeNum, iSkillNum; iPokeNum < POKEMON_PER_PLAYER; iPokeNum++ )
		{
			iPokeID = g_iPlayerPokemonStats[ iViewPlayer ][ iPokeNum ][ POKESTAT_POKEMON ];
			if ( iPokeID <= 0 )
				continue;

			iPokeCount++;

			if ( iActivePoke == iPokeNum )
				iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<b>" );

			iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "%d. %s (Lvl %d) ", iPokeCount, PokeToName( iPokeID ), g_iPlayerPokemonStats[ iViewPlayer ][ iPokeNum ][ POKESTAT_LEVEL ] );

			if ( iActivePoke == iPokeNum )
				iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "</b>" );

			iSkillCount = 0;

			for ( iSkillNum = 0; iSkillNum < SKILLS_PER_POKEMON; iSkillNum++ )
			{
				iSkillID = g_iPlayerPokemonSkills[ iViewPlayer ][ iPokeNum ][ iSkillNum ];
				if ( iSkillID <= SKILL_INVALID )
					continue;

				if ( iSkillCount )
					iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, ", " );

				iSkillCount++;
				iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, SkillToName( iSkillID ) );
			}

			iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<br>" );
		}
	}
	else
	{
		for ( new Int:i; i < iPlayerCount; i++ )
		{
			iViewPlayer = iPlayers[ i ];
			iActivePoke = PlayerGetInfo( iViewPlayer, PINFO_ACTIVE_POKENUM );

			iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "%s -", PlayerToName( iViewPlayer ) );

			for ( new Int:iPokeNum; iPokeNum < POKEMON_PER_PLAYER; iPokeNum++ )
			{
				iPokeID = g_iPlayerPokemonStats[ iViewPlayer ][ iPokeNum ][ POKESTAT_POKEMON ];
				if ( iPokeID <= 0 )
					continue;

				if ( iActivePoke == iPokeNum )
					iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<b>" );

				iLen += formatex( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "%s (Lvl %d) ", PokeToName( iPokeID ), g_iPlayerPokemonStats[ iViewPlayer ][ iPokeNum ][ POKESTAT_LEVEL ] );

				if ( iActivePoke == iPokeNum )
					iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "</b>" );
			}

			iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "<br>" );
		}
	}

	iLen += copy( szMOTD[ iLen ], charsmax(szMOTD) - iLen, "</center>" );

	show_motd( iPlayer, szMOTD, szTitle );

	pokemod_funcend_debug( "PlayerViewPlayers" );
}

PM_FUNC_NORM_ATTRIB PlayerStatHUD( Int:iPlayer )
{
	new Int:iPokeNum = PlayerGetInfo( iPlayer, PINFO_ACTIVE_POKENUM );
	if ( iPokeNum <= -1 )
		return;

	pokemod_funcstart_debug( "PlayerStatHUD" );

	new String:szMessage[ 256 ], Int:iLen;

	iLen += formatex( szMessage[ iLen ], charsmax(szMessage) - iLen, "%s Stats^n", PokeNumToName( iPokeNum, .playerID = iPlayer ) );

	new Int:iTrainBoost = PlayerGetInfo( iPlayer, PINFO_TRAINING_BOOST );
	if ( iTrainBoost )
		iLen += formatex( szMessage[ iLen ], charsmax(szMessage) - iLen, "Training Boost: +%d^n", iTrainBoost );

	for ( new PM_STAT:iStatNum = STAT_ATTACK; iStatNum < STAT_HP; iStatNum++ )
	{
		iLen += formatex( szMessage[ iLen ], charsmax(szMessage) - iLen, "%s: %d^n", gPlayerStatNames[ iStatNum ], g_iPlayerStats[ iPlayer ][ iStatNum ] );
	}

	new Int:iHudColor = PlayerGetInfo( iPlayer, PINFO_HUD_COLOR );
	if ( iHudColor == HUD_TEAM_BASED )
		iHudColor = _:cs_get_user_team( iPlayer );

	set_hudmessage( .red = cHudColors[ iHudColor ][ 0 ],
					.green = cHudColors[ iHudColor ][ 1 ],
					.blue = cHudColors[ iHudColor ][ 2 ],
					.x = 0.99,
					.y = -1.0,
					.effects = 0,
					.fxtime = 0.1,
					.holdtime = 10.0,
					.fadeintime = 0.1,
					.fadeouttime = 0.1,
					.channel = -1
	);

	ShowSyncHudMsg( iPlayer, g_iStatHudSync, szMessage );

	pokemod_funcend_debug( "PlayerStatHUD" );
}



